package net.aiion.weave;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;

import net.aiion.weave.asm.ClassReader;
import net.aiion.weave.asm.util.ASMifier;
import net.aiion.weave.asm.util.TraceClassVisitor;
import net.aiion.weave.impl.conversions.transformation.ConverterTransformation;
import net.aiion.weave.impl.injection.transformation.InjectorTransformation;
import net.aiion.weave.impl.messaging.transformation.MessageReceiverTransformation;
import net.aiion.weave.spi.bytecode.BytecodeSink;
import net.aiion.weave.spi.bytecode.BytecodeSinkFactory;
import net.aiion.weave.spi.bytecode.BytecodeSourceFactory;

public class MainProgram
{
    public static void main(String[] args) throws ClassNotFoundException {
        // try {
        // final ClassReader cr = new ClassReader(PublishableClass.class.getName());
        // final TraceClassVisitor cv = new TraceClassVisitor(null, new ASMifier(), new PrintWriter(
        // new OutputStreamWriter(System.out)));
        // cr.accept(cv, 0);
        // } catch (IOException e) {
        // e.printStackTrace();
        // }
        // System.exit(0);

        final ClassLoader cl = new BytecodeClassLoader(Thread.currentThread().getContextClassLoader());
        final Class<?> c = cl.loadClass("net.aiion.weave.A");
        System.out.println("Ok: " + c.getName());
        try {
			Object o = c.getDeclaredConstructor(S.class).newInstance(new S());
			print((String) c.getDeclaredField("foo").get(o));
		} catch (InstantiationException | IllegalAccessException
				| IllegalArgumentException | InvocationTargetException
				| NoSuchMethodException | SecurityException | NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
    
    public static void print(String s) {
    	System.out.println(s);
    }
}

class BytecodeClassLoader extends ClassLoader
{
    private String[] ignoredPackages = {
        "java.", "javax.", "sun."
    };

    public BytecodeClassLoader() {
        super(ClassLoader.getSystemClassLoader());
    }

    public BytecodeClassLoader(final ClassLoader aParentClassLoader) {
        super(aParentClassLoader);
    }

    @Override
    protected Class<?> loadClass(final String aClassName, final boolean aResolve) throws ClassNotFoundException {
        Class<?> loadedClass = findLoadedClass(aClassName);
        if (loadedClass == null) {
            for (String ignoredPackage : ignoredPackages) {
                if (aClassName.startsWith(ignoredPackage)) {
                    loadedClass = super.loadClass(aClassName, aResolve);
                }
            }
        }

        if (loadedClass == null) {
            loadedClass = loadClass1(aClassName);
        }
        
        if (loadedClass == null) {
        	loadedClass = super.loadClass(aClassName, aResolve);
        }

        if (aResolve) {
            resolveClass(loadedClass);
        }

        return loadedClass;
    }

    private Class<?> loadClass1(final String aClassName) throws ClassNotFoundException {
        System.out.println("Loading " + aClassName);
        byte[] classBytes = null;
        if ("net.aiion.weave.A".equals(aClassName)) {
        	try {
				classBytes = InjectingClassDump.dump();
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
        }

        if (classBytes == null) {
            return null;
        }

        // BEGIN DEBUG
        try {
            final ClassReader cr = new ClassReader(new ByteArrayInputStream(classBytes));
            final TraceClassVisitor cv = new TraceClassVisitor(null, new ASMifier(), new PrintWriter(
                new OutputStreamWriter(System.out)));
            cr.accept(cv, 0);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // END DEBUG

        return defineClass(aClassName, classBytes, 0, classBytes.length);
    }
}
